package com.lixixi.tingshu.account.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lixixi.tingshu.account.mapper.UserAccountMapper;
import com.lixixi.tingshu.account.service.*;
import com.lixixi.tingshu.album.api.AlbumApiFeignClient;
import com.lixixi.tingshu.common.constant.RedisConstant;
import com.lixixi.tingshu.common.constant.SystemConstant;
import com.lixixi.tingshu.common.result.Result;
import com.lixixi.tingshu.common.result.ResultCodeEnum;
import com.lixixi.tingshu.common.util.AuthContextHolder;
import com.lixixi.tingshu.model.account.UserAccount;
import com.lixixi.tingshu.model.account.UserAccountDetail;
import com.lixixi.tingshu.user.api.UserApiFeignClient;
import com.lixixi.tingshu.vo.account.AccountLockResultVo;
import com.lixixi.tingshu.vo.account.AccountLockVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

	@Autowired
	UserAccountDetailService userAccountDetailService;

	@Autowired
	AlbumApiFeignClient albumApiFeignClient;

	@Autowired
	RedisTemplate<String, String> redisTemplate;

	@Autowired
	private UserAccountMapper userAccountMapper;
	@Autowired
	private UserApiFeignClient userApiFeignClient;
	@Autowired
	private UserAccountService userAccountService;

	@Override
	@Transactional
	public Result checkAndLock(AccountLockVo accountLockVo) {
		//获取订单号
		String orderNo = accountLockVo.getOrderNo();
		//获取订单金额
		BigDecimal paidAmount = accountLockVo.getAmount();
		//获取用户id
		Long userId = AuthContextHolder.getUserId();
		//使用 SETNX 保存订单
		Boolean absent = redisTemplate.opsForValue().setIfAbsent(RedisConstant.CHECK_LOCK_PREFIX + orderNo, String.valueOf(1), 1, TimeUnit.HOURS);
		if (Boolean.FALSE.equals(absent))
		{
			//说明feign接口执行重试机制
			//直接从缓存中获取后余额结果返回计算
			String accountLockResultVoStr = redisTemplate.opsForValue().get(RedisConstant.ACCOUNT_LOCK_PREFIX + orderNo);
			if (StringUtils.isNotBlank(accountLockResultVoStr))
			{
				AccountLockResultVo accountLockResultVo = JSONObject.parseObject(accountLockResultVoStr, AccountLockResultVo.class);
				//构建返回对象
				return Result.ok(accountLockResultVo);
			} else
			{
				//为空说明业务还未执行结束
				//休眠200ms
				try
				{
					Thread.sleep(200);
				} catch (InterruptedException e)
				{
					//出现异常
					e.printStackTrace();
					//返回错误信息
					return Result.fail(ResultCodeEnum.SERVICE_ERROR);
				}
				//自旋
				checkAndLock(accountLockVo);
			}
		}
		//获取用户账户信息,对当前记录使用悲观锁
		UserAccount userAccount = userAccountMapper.getAccountForUpdate(userId);
		//检查用户可用余额
		BigDecimal availableAmount = userAccount.getAvailableAmount();
		if (availableAmount.compareTo(availableAmount) < 0)
		{
			//删除锁定金额缓存
			redisTemplate.delete(RedisConstant.CHECK_LOCK_PREFIX + orderNo);
			//返回余额不足
			return Result.fail(ResultCodeEnum.ACCOUNT_LESS);
		}
		//记录用户账户明细
		UserAccountDetail userAccountDetail = new UserAccountDetail();
		userAccountDetail.setAmount(paidAmount);
		userAccountDetail.setUserId(userId);
		userAccountDetail.setTitle("锁定: " + accountLockVo.getContent());
		userAccountDetail.setOrderNo(orderNo);
		userAccountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_LOCK);
		this.log(userAccountDetail);
		//锁定金额
		LambdaUpdateWrapper<UserAccount> userAccountLambdaUpdateWrapper = new LambdaUpdateWrapper<UserAccount>()
				.eq(UserAccount::getUserId, userId)
				.setSql("available_amount = available_amount - " + paidAmount)
				.setSql("lock_amount = lock_amount + " + paidAmount);
		boolean update = this.update(userAccountLambdaUpdateWrapper);
		//更新失败, 返回锁定金额失败
		if (!update)
		{
			//删除锁定金额缓存
			redisTemplate.delete(RedisConstant.CHECK_LOCK_PREFIX + orderNo);
			//记录金额解锁
			userAccountDetail.setTitle("解锁: " + accountLockVo.getContent());
			userAccountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK);
			log(userAccountDetail);
			//返回锁定金额失败
			return Result.fail(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
		}
		//锁定成功,将锁定金额添加至缓存
		redisTemplate.opsForValue().set(RedisConstant.ACCOUNT_LOCK_PREFIX + orderNo, paidAmount.toString());
		//构建锁定金额返回对象
		AccountLockResultVo lockResultVo = new AccountLockResultVo();
		lockResultVo.setUserId(userId);
		lockResultVo.setAmount(paidAmount);
		lockResultVo.setContent(accountLockVo.getContent());
		//保存至缓存, 方便后续记录账户明细, 用户金额扣减
		redisTemplate.opsForValue().set(RedisConstant.ACCOUNT_LOCK_PREFIX + orderNo, JSONObject.toJSONString(lockResultVo), 1, TimeUnit.HOURS);
		return Result.ok(lockResultVo);
	}

	@Override
	@Transactional
	public void log(UserAccountDetail userAccountDetail) {
		userAccountDetailService.save(userAccountDetail);
	}

	@Override
	public void minus(String orderNo) {
		//根据缓存获取订单锁定金额明细
		String lockResultVo = redisTemplate.opsForValue().get(RedisConstant.ACCOUNT_LOCK_PREFIX + orderNo);
		AccountLockResultVo accountLockResultVo = JSONObject.parseObject(lockResultVo, AccountLockResultVo.class);
		Long userId = accountLockResultVo.getUserId();
		BigDecimal amount = accountLockResultVo.getAmount();
		String content = accountLockResultVo.getContent();
		//扣减支付金额
		LambdaUpdateWrapper<UserAccount> userAccountLambdaUpdateWrapper = new LambdaUpdateWrapper<UserAccount>()
				.setSql("total_amount = total_amount - " + amount)
				.setSql("lock_amount = lock_amount - " + amount)
				.eq(UserAccount::getUserId, userId);
		userAccountService.update(userAccountLambdaUpdateWrapper);
		//记录用户账户明细
		//记录解锁信息
		UserAccountDetail userAccountDetail = new UserAccountDetail();
		userAccountDetail.setAmount(amount);
		userAccountDetail.setUserId(userId);
		userAccountDetail.setTitle("解锁: " + content);
		userAccountDetail.setOrderNo(orderNo);
		userAccountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK);
		this.log(userAccountDetail);
		//记录消费信息
		userAccountDetail.setId(null);
		userAccountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_MINUS);
		userAccountDetail.setTitle("消费: " + content);
		this.log(userAccountDetail);
	}

	@Override
	public void unlock(AccountLockVo accountLockVo) {
		BigDecimal amount = accountLockVo.getAmount();
		Long userId = accountLockVo.getUserId();
		String content = accountLockVo.getContent();
		String orderNo = accountLockVo.getOrderNo();
		//解锁账户锁定金额
		LambdaUpdateWrapper<UserAccount> userAccountLambdaUpdateWrapper = new LambdaUpdateWrapper<UserAccount>()
				.setSql("available_amount = available_amount + " + amount)
				.setSql("lock_amount = lock_amount - " + amount)
				.eq(UserAccount::getUserId, userId);
		userAccountService.update(userAccountLambdaUpdateWrapper);
		//记录用户账户明细
		//记录解锁信息
		UserAccountDetail userAccountDetail = new UserAccountDetail();
		userAccountDetail.setAmount(amount);
		userAccountDetail.setUserId(userId);
		userAccountDetail.setTitle("解锁: " + content);
		userAccountDetail.setOrderNo(orderNo);
		userAccountDetail.setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK);
		this.log(userAccountDetail);
	}

}
